package com.homedo.odin.solution.service.bizservice.personal;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.homedo.common.redis.service.RedisService;
import com.homedo.core.common.base.dto.Meta;
import com.homedo.core.common.exception.BizException;
import com.homedo.core.common.util.BeanUtils;
import com.homedo.core.common.util.DateUtils;
import com.homedo.core.common.util.HttpClient;
import com.homedo.core.common.util.ParamCheckUtil;
import com.homedo.odin.solution.config.PlatformConfigHelper;
import com.homedo.odin.solution.config.SysConfigHelper;
import com.homedo.odin.solution.contract.personal.treasure.AddScoreReq;
import com.homedo.odin.solution.contract.personal.treasure.LevelRuleResp;
import com.homedo.odin.solution.contract.personal.treasure.PrivilegeResp;
import com.homedo.odin.solution.contract.personal.treasure.ScoreLevelResp;
import com.homedo.odin.solution.contract.personal.treasure.ScoreRuleResp;
import com.homedo.odin.solution.enumcode.TemplateCodeEnum;
import com.homedo.odin.solution.enumcode.basic.ErrorCodeEnum;
import com.homedo.odin.solution.enumcode.other.ScoreRuleEnum;
import com.homedo.odin.solution.model.dto.InviteDetail;
import com.homedo.odin.solution.model.dto.LevelRule;
import com.homedo.odin.solution.model.dto.ScoreLevelPrivilege;
import com.homedo.odin.solution.model.entity.UserBasic;
import com.homedo.odin.solution.model.entity.UserProfile;
import com.homedo.odin.solution.model.entity.UserScore;
import com.homedo.odin.solution.model.entity.UserScoreDetail;
import com.homedo.odin.solution.model.entity.UserScoreLevel;
import com.homedo.odin.solution.service.ScoreLevelServiceImpl;
import com.homedo.odin.solution.service.UserBasicServiceImpl;
import com.homedo.odin.solution.service.UserPopularityDetailServiceImpl;
import com.homedo.odin.solution.service.UserProfileServiceImpl;
import com.homedo.odin.solution.service.UserScoreDetailServiceImpl;
import com.homedo.odin.solution.service.UserScoreLevelServiceImpl;
import com.homedo.odin.solution.service.UserScoreServiceImpl;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 * @author: zhaoxudong
 * @date: 2018/3/7 19:05
 * @Description: 我的财富业务处理
 */
@Service
public class UserTreasureBizService {

    private final static Logger logger = LoggerFactory.getLogger(UserTreasureBizService.class);
    public static final String CODE = "code";
    public static final String TOTAL = "total";
    @Autowired
    UserScoreServiceImpl userScoreService;
    @Autowired
    ScoreLevelServiceImpl helper;
    @Autowired
    UserScoreLevelServiceImpl levelService;
    @Autowired
    UserScoreDetailServiceImpl userScoreDetailService;
    @Autowired
    UserProfileServiceImpl profileService;
    @Autowired
    UserPopularityDetailServiceImpl popularityDetailService;
    @Autowired
    UserBasicServiceImpl basicService;
    @Autowired
    CommonBizService commonBizService;
    @Autowired
    PlatformConfigHelper platformConfigHelper;
    @Autowired
    SysConfigHelper sysConfigHelper;
    @Autowired
    RedisService redisService;
    @Autowired
    ScoreLevelServiceImpl scoreLevelServiceImpl;

    public ScoreLevelResp getScoreLevel(Integer userId, String token) {
        ParamCheckUtil.checkUserId(userId);
        if (StringUtils.isBlank(token)) {
            throw new BizException(Meta.CODE_451.getCode(), "token不能为空");
        }
        UserScore score = getUserScore(userId);
        if (score == null) {
            score = new UserScore();
            score.setScoreLevel(1);
            score.setTotal(0);
            score.setUserId(userId);
            score.setModifiedBy(userId);
            score.setCreatedBy(userId);
            userScoreService.insert(score);
        }
        //用邀请注册用户是在平台方的动作，增加积分的动作，需要在这里做。
        Integer total;
        try {
            total = addInviteScore(userId, score, token);
        } catch (ParseException e) {
            throw new BizException(Meta.CODE_500.getCode(), e.getMessage());
        }
        ScoreLevelResp resp = new ScoreLevelResp();
        resp.setScore(total);
        LevelRule level = helper.getScoreLevelByScore(total);
        resp.setLevelName(level.getLevelName());
        resp.setLevel(level.getLevel());

        //获取用户当前积分，更新积分并需要判断是否升级
        score.setTotal(total);
        score.setScoreLevel(level.getLevel());
        score.setModifiedTime(new Date());
        userScoreService.updateById(score);

        return resp;
    }

    /**
     * 增加邀请注册积分
     */
    private Integer addInviteScore(Integer userId, UserScore userScore, String token) throws ParseException {
        Integer currentTotal = userScore.getTotal();
        // 1. 判断邀请用户数是否已达上限
        Integer count = userScoreDetailService.selectCountByTypeAndUserId(userId, ScoreRuleEnum.Invite.getType());
        if (count >= ScoreRuleEnum.Invite.getLimit()) {
            return currentTotal;
        }
        //2. 从平台获取邀请人列表
        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
        //加入用户标识
        param.add("key", token);
        //加入分页参数取前五条
        JSONObject pageParam = new JSONObject();
        //第一页
        pageParam.put("p", 1);
        //每页五条数据
        pageParam.put("s", 5);
        param.add("jdata", pageParam.toJSONString());
        logger.info("获取用户推荐好友信息列表请求参数：{}", JSON.toJSONString(param));
        String inviteList = HttpClient.post(platformConfigHelper.getRecommendedRecordListUrl(), param);
        logger.info("获取用户推荐好友信息列表返回结果：{}", JSON.toJSONString(inviteList));
        JSONObject result = JSON.parseObject(inviteList.replace("(",
                "").replace(")", ""));
        // 3. 解析数据
        // 失败,直接返回
        if (result.getIntValue(CODE) == 0) {
            return currentTotal;
        }
        // 获取data
        JSONObject data = JSONObject.parseObject(result.getString("data"));
        // 没有数据,直接返回
        if (data.getIntValue(TOTAL) == 0) {
            return currentTotal;
        }
        // 解析详细数据
        List<InviteDetail> detailList = JSONObject.parseArray(data.getString("rows"), InviteDetail.class);
        Date startTime = DateUtils.parseDate(sysConfigHelper.getScoreStartTime());
        Date insertTime;
        UserScoreDetail scoreDetail;
        for (InviteDetail detail : detailList) {
            insertTime = DateUtils.parseDate(detail.getInsertTime());
            // 因为加入时间是按倒序排列，第一个不符合要求，其他无需校验。直接跳出循环即可
            if (insertTime.before(startTime)) {
                logger.info("邀请注册时间不合要求:{}", detail.toJson());
                break;
            }
            //判断是否已经加过积分
            scoreDetail = userScoreDetailService
                    .findByUserIdAndBizCodeAndType(userId, detail.getName(), ScoreRuleEnum.Invite.getType());
            //已增加过积分，跳出循环
            if (scoreDetail != null) {
                logger.info("邀请已经增加过积分：{}", detail.toJson());
                break;
            }
            // 增加积分
            AddScoreReq addScoreReq = new AddScoreReq();
            addScoreReq.setBizCode(detail.getName());
            addScoreReq.setType(ScoreRuleEnum.Invite.getType());
            addScoreReq.setUserId(userId);
            addScore(addScoreReq);

            currentTotal += ScoreRuleEnum.Invite.getScore();
        }
        return currentTotal;
    }

    private UserScore getUserScore(Integer userId) {
        UserScore score = userScoreService.selectByUserId(userId);
        return score;
    }

    /**
     * 获取等级规则
     */
    public LevelRuleResp getLevelRule() {
        LevelRuleResp resp = new LevelRuleResp();
        resp.setLevelRules(helper.getRuleList());
        return resp;
    }

    /**
     * 获取积分等级特权
     */
    public PrivilegeResp getPrivilege() {
        PrivilegeResp resp = new PrivilegeResp();
        List<ScoreLevelPrivilege> privilegeList = scoreLevelServiceImpl.getPrivilegeList();
        resp.setPrivileges(privilegeList);
        return resp;
    }

    /**
     * 查询获取积分情况
     */
    public ScoreRuleResp getScoreRule(Integer userId) {
        ParamCheckUtil.checkUserId(userId);
        ScoreRuleResp resp = new ScoreRuleResp();
        //判断今天是否已签到
        Integer isSignIn = 0;
        String today = DateUtils.formatDate(new Date(), "yyyy-MM-dd");
        UserScoreDetail detail = userScoreDetailService
                .selectBySignInAndDate(userId, today, ScoreRuleEnum.SingIn.getType());
        if (detail != null) {
            isSignIn = 1;
        }
        //获取上传方案数量
        Integer solCount = userScoreDetailService.selectCountByTypeAndUserId(userId, ScoreRuleEnum.Sol.getType());
        //点赞数
        Integer startCount = userScoreDetailService.selectCountByTypeAndUserId(userId, ScoreRuleEnum.Start.getType());
        //评论数
        Integer commentCount = userScoreDetailService
                .selectCountByTypeAndUserId(userId, ScoreRuleEnum.Comment.getType());
        //邀请注册用户数
        Integer inviteCount = userScoreDetailService.selectCountByTypeAndUserId(userId, ScoreRuleEnum.Invite.getType());
        //发布悬赏数
        Integer bountyCount = userScoreDetailService.selectCountByTypeAndUserId(userId, ScoreRuleEnum.Bounty.getType());
        resp.setBountyCount(bountyCount);
        resp.setCommentCount(commentCount);
        resp.setInviteCount(inviteCount);
        resp.setSolCount(solCount);
        resp.setStartCount(startCount);
        resp.setIsSignIn(isSignIn);
        return resp;
    }

    /**
     * 增加积分，提升等级
     */
    @Transactional(rollbackFor = {Exception.class, RuntimeException.class})
    public void addScore(AddScoreReq request) {
        logger.info("增加积分请求参数:{}", request.toJson());
        Integer userId = request.getUserId();
        Integer type = request.getType();
        ParamCheckUtil.checkUserId(userId);
        if (type == null) {
            logger.info("积分类型不能为空");
            throw new BizException(Meta.CODE_451.getCode(), "积分类型不能为空");
        }
        UserBasic userBasic = basicService.selectByUserId(userId);
        if (userBasic == null) {
            logger.info("不存在该用户，请确认userId");
            throw new BizException(Meta.CODE_451.getCode(), "不存在该用户，请确认userId");
        }
        ScoreRuleEnum ruleEnum = getRuleEnum(type);
        //判断该积分类型是不是已经达到上限
        Integer count = userScoreDetailService.selectCountByTypeAndUserId(userId, ruleEnum.getType());
        if (count >= ruleEnum.getLimit()) {
            //已经达到或超过上限，直接返回
            logger.info("用户获取积分已达到上限，获取方式：{},userId:{}", ruleEnum.getType(), userId);
            return;
        }
        if (type.equals(ScoreRuleEnum.SingIn.getType())) {
            //如果签到，需要判断当天是否已签到
            UserScoreDetail detail = userScoreDetailService.findByTodaySingIn(type, userId);
            if (detail != null) {
                //已经签到
                logger.info("用户重复签到，userId:{}", userId);
                throw new BizException(ErrorCodeEnum.IsExist.getDbValue(), "重复签到");
            }
        }
        //在积分详情表中插入数据
        userScoreDetailService.insert(userId, type, ruleEnum.getScore(), request.getBizCode());
        //获取用户当前积分，并更新
        UserScore userScore = getUserScore(userId);
        if (userScore == null) {
            //第一次使用，新增积分数据
            userScore = new UserScore();
            userScore.setScoreLevel(1);
            userScore.setTotal(ruleEnum.getScore());
            userScore.setUserId(userId);
            userScore.setModifiedBy(userId);
            userScore.setCreatedBy(userId);
            userScoreService.insert(userScore);
            return;
        }
        //已有积分数据,更新积分并需要判断是否升级
        Integer currentLevel = userScore.getScoreLevel();
        Integer score = userScore.getTotal() + ruleEnum.getScore();
        LevelRule level = helper.getScoreLevelByScore(score);
        userScore.setTotal(score);
        userScore.setScoreLevel(level.getLevel());
        userScore.setModifiedTime(new Date());
        userScoreService.updateById(userScore);
        //用户达到二级，新建用户主页
        if (currentLevel != 2 && level.getLevel() == 2) {
            logger.info("新建用户主页！userId:{}", userId);
            UserProfile profile = profileService.selectByUserId(userId);
            if (profile == null) {
                profile = new UserProfile();
            }
            profile.setUserId(userId);
            profile.setModifiedBy(userId);
            profile.setCreatedBy(userId);
            //获取人气值
            Integer clickCount = popularityDetailService.selectScoreCountByUserId(userId);
            profile.setClickCount(clickCount);
            profile.setModifiedTime(new Date());
            profileService.insertOrUpdate(profile);
        }
        //用户升级，发送消息
        if (level.getLevel() > currentLevel) {
            logger.info("用户升级了！userId:{}", userId);
            Map<String, String> map = new HashMap<>();
            map.put("@@userName",
                    StringUtils.isBlank(userBasic.getNickName()) ? userBasic.getUserName() : userBasic.getNickName());
            map.put("@@levelName", level.getLevelName());
            map.put("@@count", score.toString());
            commonBizService.sentMessage(map, userId, 0, TemplateCodeEnum.level_up.toString());
        }
    }

    private ScoreRuleEnum getRuleEnum(Integer type) {
        for (ScoreRuleEnum ruleEnum : ScoreRuleEnum.values()) {
            if (ruleEnum.getType().equals(type)) {
                return ruleEnum;
            }
        }
        //如果没有找到对应的枚举，说明积分类型传的有问题
        throw new BizException(Meta.CODE_451.getCode(), "积分类型不正确");
    }

    /**
     * 获取用户等级特权
     */
    public ScoreLevelPrivilege getPrivilegeByUserId(Integer userId) {
        ParamCheckUtil.checkUserId(userId);
        UserScoreLevel scoreLevel = levelService.selectByUserId(userId);
        if (scoreLevel == null) {
            //用户第一次使用需要新增
            UserScore userScore = new UserScore();
            userScore.setScoreLevel(1);
            userScore.setTotal(0);
            userScore.setUserId(userId);
            userScore.setModifiedBy(userId);
            userScore.setCreatedBy(userId);
            userScoreService.insert(userScore);
            scoreLevel = levelService.selectByUserId(userId);
        }
        return BeanUtils.transfrom(ScoreLevelPrivilege.class, scoreLevel);
    }
}
